<?php

/*
 *	Framework namespace
 */
namespace Nano
{
	/*
	 *	QR Barcode encoding library
	 *
	 *	Based on http://www.e-luge.net/blog/full/655063.html
	 */
	class QR
	{
		/*
		 *	$data	string	Data to encode
		 */
		private $data;

		/*
		 *	$datalength	int	Lenght of data
		 */
		private $datalength = 0;

		/*
		 *	$ecc	char	Error correction level [L|M|Q|H]
		 */
		private $ecc = 'M';

		/*
		 *	$size	int	Image size
		 */
		private $size = 10;

		/*
		 *	$type	string	Image type [png|jpeg]
		 */
		private $type = 'png';

		/*
		 *	$path	string	Path to *.dat files (relative to application)
		 */
		private $path = 'qr';

		/*
		 *	$ecc_hash	array	Correction error level
		 */
		private static $ecc_hash = array
		(
			'l' => '1',
			'm' => '0',
			'q' => '3',
			'h' => '2'
		);

		/*
		 *	$alphanumeric_hash	array	Symbols encoding
		 */
		private static $alphanumeric_hash = array
		(
			'0' => 0,
			'1' => 1,
			'2' => 2,
			'3' => 3,
			'4' => 4,
			'5' => 5,
			'6' => 6,
			'7' => 7,
			'8' => 8,
			'9' => 9,
			'A' => 10,
			'B' => 11,
			'C' => 12,
			'D' => 13,
			'E' => 14,
			'F' => 15,
			'G' => 16,
			'H' => 17,
			'I' => 18,
			'J' => 19,
			'K' => 20,
			'L' => 21,
			'M' => 22,
			'N' => 23,
			'O' => 24,
			'P' => 25,
			'Q' => 26,
			'R' => 27,
			'S' => 28,
			'T' => 29,
			'U' => 30,
			'V' => 31,
			'W' => 32,
			'X' => 33,
			'Y' => 34,
			'Z' => 35,
			' ' => 36,
			'$' => 37,
			'%' => 38,
			'*' => 39,
			'+' => 40,
			'-' => 41,
			'.' => 42,
			'/' => 43,
			':' => 44,
			
		);

		/*
		 *	$data_bits	array	Bits data
		 */
		private static $data_bits = array
		(
			0, 128, 224, 352, 512, 688, 864, 992, 1232, 1456, 1728, 
			2032, 2320, 2672, 2920, 3320, 3624, 4056, 4504, 5016, 5352, 
			5712, 6256, 6880, 7312, 8000, 8496, 9024, 9544, 10136, 10984, 
			11640, 12328, 13048, 13800, 14496, 15312, 15936, 16816, 17728, 18672, 
		
			152, 272, 440, 640, 864, 1088, 1248, 1552, 1856, 2192, 
			2592, 2960, 3424, 3688, 4184, 4712, 5176, 5768, 6360, 6888, 
			7456, 8048, 8752, 9392, 10208, 10960, 11744, 12248, 13048, 13880, 
			14744, 15640, 16568, 17528, 18448, 19472, 20528, 21616, 22496, 23648, 
		
			72, 128, 208, 288, 368, 480, 528, 688, 800, 976, 
			1120, 1264, 1440, 1576, 1784, 2024, 2264, 2504, 2728, 3080, 
			3248, 3536, 3712, 4112, 4304, 4768, 5024, 5288, 5608, 5960, 
			6344, 6760, 7208, 7688, 7888, 8432, 8768, 9136, 9776, 10208, 
		
			104, 176, 272, 384, 496, 608, 704, 880, 1056, 1232, 
			1440, 1648, 1952, 2088, 2360, 2600, 2936, 3176, 3560, 3880, 
			4096, 4544, 4912, 5312, 5744, 6032, 6464, 6968, 7288, 7880, 
			8264, 8920, 9368, 9848, 10288, 10832, 11408, 12016, 12656, 13328
		);

		/*
		*	$format_inf	array	Breakdown of bits
		*/
		private static $format_inf = array
		(
			'101010000010010',
			'101000100100101',
			'101111001111100',
			'101101101001011',
			'100010111111001',
			'100000011001110',
			'100111110010111',
			'100101010100000',
			'111011111000100',
			'111001011110011',
			'111110110101010',
			'111100010011101',
			'110011000101111',
			'110001100011000',
			'110110001000001',
			'110100101110110',
			'001011010001001',
			'001001110111110',
			'001110011100111',
			'001100111010000',
			'000011101100010',
			'000001001010101',
			'000110100001100',
			'000100000111011',
			'011010101011111',
			'011000001101000',
			'011111100110001',
			'011101000000110',
			'010010010110100',
			'010000110000011',
			'010111011011010',
			'010101111101101'
		);

		/*
		 *	$patterns	array	QR Barcode template
		 */
		private $patterns = array
		(
			'b' => 'iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAMAAACeL25MAAADAFBMVEUAAACAgICAAACAgAAAgAAAgIAAAICAAICAgEAAQEAAgP8AQIBAAP+AQAD////AwMD/AAD//wAA/wAA//8AAP//AP///4AA/4CA//+AgP//AID/gEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkHZX0AAAADklEQVR4nGPgAwIGVAIACNQA4aH1GJsAAAAASUVORK5CYII=',
			'd' => 'iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAMAAACeL25MAAADAFBMVEUAAACAgICAAACAgAAAgAAAgIAAAICAAICAgEAAQEAAgP8AQIBAAP+AQAD////AwMD/AAD//wAA/wAA//8AAP//AP///4AA/4CA//+AgP//AID/gEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABkHZX0AAAAC0lEQVR4nGNgwAQAABQAAX3+Hu4AAAAASUVORK5CYII=',
			'qrv1' => 'iVBORw0KGgoAAAANSUhEUgAAAB0AAAAdAQMAAABsXfVMAAAABlBMVEUAAAD///+l2Z/dAAAAPklEQVR4nGP4////DwY04oP8gQqG7/fvVzB8ib+ITIDFPog6VEAV34drQ2N9B5sCJL6DuF/iUQiwGFgWi+UA1Klkqp3gt4kAAAAASUVORK5CYII=',
			'qrv10' => 'iVBORw0KGgoAAAANSUhEUgAAAEEAAABBAQMAAAC0OVsGAAAABlBMVEUAAAD///+l2Z/dAAAAmElEQVR4nGP4DwUNDPhZH+RB9Bn2Bobv90Gst98bGL7Eg1hnxWGstxDWH3kwC6ju7/3/d4HqPoiGAkEIO9i8v/chJt8HqUOz7T4WFwwmMXaQmz/UA1nlIH98B7HC//+N//8VVQyhbuDdjEXs63wY60M9kjqYm7/D/AGMc6jfIHEJEoPE74d6WJyD9CJY3+G2QdIL4XQFZwEAAyEBzAFPbYcAAAAASUVORK5CYII=',
			'qrv11' => 'iVBORw0KGgoAAAANSUhEUgAAAEUAAABFAQMAAAAmQ7lqAAAABlBMVEUAAAD///+l2Z/dAAAAlElEQVR4nGP4DwU/GPCzPsiD6H8OFQzf74NZ8RUMX+JBrD+BMNZfKIv9/5+LEHXl///er2D4IBoKAkC9IFXlEJPvg9Sh2XYfiwsGuxg72B/sQFY52G/lQFb4fzBCEUOoG3g3Eyn2Rx7G+lgPY324D/fHd5jfQGkD4l9InIPEIOkApA6SNkB6EazvcNs+wO1Acgs+FgDnczyR2YwyRwAAAABJRU5ErkJggg==',
			'qrv12' => 'iVBORw0KGgoAAAANSUhEUgAAAEkAAABJAQMAAABLvZmfAAAABlBMVEUAAAD///+l2Z/dAAAAlUlEQVR4nGP4DwMNDASZH+TBjDfsDQzf74OZX783MHyJBzOvisOZZ6HMg0AF4hC1d4EIqPaDaCgIhLBDzL0LteI+WC26xfexOmd4iLKDffyhHsgsB4fDdxAzHBiMwDBDE0VSO8h8QbHon/lw5jeE6M94hI+/w8MBlPqgoQNNUSDRLwi10NQHMgGJ+R1hMTT9EpXWEUwAhOmSBu1sT6EAAAAASUVORK5CYII=',
			'qrv13' => 'iVBORw0KGgoAAAANSUhEUgAAAE0AAABNAQMAAADZx3vzAAAABlBMVEUAAAD///+l2Z/dAAAAlklEQVR4nGP4DwM/GAgyP8iDGX8cKhi+34cI3q9g+BIPZv29CGf+D4QwP9T//xsIUfsdyASq/SAaCgZAE0DqgKJgK+6D1aJbfB+rc4azKDskHNiBzHJI6JQDmeH//38FMsNRRZHUDjJf0Ej0qzyc+R0h+i0eEQ7f4aEDSqnQMIOmPpDoF4RaWEr9j8KEpWpEWicqXyCYAIO00tSNZcuBAAAAAElFTkSuQmCC',
			'qrv14' => 'iVBORw0KGgoAAAANSUhEUgAAAFEAAABRAQMAAACQQdh1AAAABlBMVEUAAAD///+l2Z/dAAAAnUlEQVR4nGP4DwcNDMSwP8hDWFfYGxi+34ew735vYPgSD2H/FUewz0LYH+r/s/9/Kg5W/73+fzlY/QfRUDAIYQebDxYH23Ufoh7DDfdxuG2wibND3P+hHsguh/jrO4gd/v9rPZhAE0dWPxjcP+pfMPvTfAT7Xz2C/T0eyf3fEf4C5gu4f2HpHCT+BUk9LF+AzEFmw/IRcv4iNj8isQEivhD8FrGUxwAAAABJRU5ErkJggg==',
			'qrv15' => 'iVBORw0KGgoAAAANSUhEUgAAAFUAAABVAQMAAAACOzoZAAAABlBMVEUAAAD///+l2Z/dAAAAnklEQVR4nGP4Dwc/GIhhf5CHMg9UMHy/D2H+ja9g+BIPFQ/EYH+o/3/w/59AsPrv9f/v/v97v4Lhg2goBDhUgMwHi4Ptug9Rj+GG+zjcNtjE2aHuZweyy6H+Kgeyw/9/rf9/9f//cDRxZPWDwf2j4UCW+G95JPF6BPtHPJK/viP8C8xH8HCA5QuQ+Bck9fB89B+VDct3KPmRyPyLxAYA0JxnyKZ5eLwAAAAASUVORK5CYII=',
			'qrv16' => 'iVBORw0KGgoAAAANSUhEUgAAAFkAAABZAQMAAABvxRrsAAAABlBMVEUAAAD///+l2Z/dAAAAnElEQVR4nGP4jwANDERyPshDmX/YGxi+34dyjn5vYPgSD+VcFcfkfKgHob/iYD3f60HoLVDPB9FQCAhhB9sDkQFbeh+qB9M595E5Q0aGHeqfD/VATjnUp99BnPD/X+shCF0GRc8g8w+9ZEbDDY/MD2SZ3/tRZZDC4DtS6ABzMCLc4LkRJAPPpx/qkXIwyDQUzndkS+HlAQllCDIHAMoLwOeLbQvLAAAAAElFTkSuQmCC',
			'qrv17' => 'iVBORw0KGgoAAAANSUhEUgAAAF0AAABdAQMAAAD9v/iAAAAABlBMVEUAAAD///+l2Z/dAAAAmUlEQVR4nGP4jwA/GIjkfJCHMv86VDB8vw/l/LlfwfAlHiZzEYnzD8phByGQDEhPOQj9A+r5IBoKBUDTQMrBMmBL70P1YDrnPjJnWMuww8KAHcgph4VOOZAT/h+G0GVQ9Awy/9BLZjTc8Mh8jEfi/NqPxAFlTUQYfEcKHVCuh4cbPAeDZOB5G6TnC7LRKBx4SYFShpBQ7iBzANArDxDm87+yAAAAAElFTkSuQmCC',
			'qrv18' => 'iVBORw0KGgoAAAANSUhEUgAAAGEAAABhAQMAAAD8yF3gAAAABlBMVEUAAAD///+l2Z/dAAAAo0lEQVR4nGP4jwQaGIjnfZCHsY+wNzB8vw/jvf3ewPAlHsb7K47Muwrlsf//fxCoUhyir/z//7sQfR9EQ6EghB1iH0QObPt9mD4sLruPwhtZcuywcPlQD+SVw8LsO4gX/h+Erv7//xVTDlXf4PXfYJIbDWuqyn1Dkftsjy6HFGbfkcMTVPYgwhpRhoDkEOXLh3rksgdkJioPUWahlmeklYMoPAChVH7FEFQF8gAAAABJRU5ErkJggg==',
			'qrv19' => 'iVBORw0KGgoAAAANSUhEUgAAAGUAAABlAQMAAABusr+MAAAABlBMVEUAAAD///+l2Z/dAAAAp0lEQVR4nGP4jwR+MBDP+yAPY/87UMHw/T6cF1/B8CUerjAQmffvIoTH/v//h/r/fy5C9JX///+9/v+/+xUMH0RDYcChAmwfRA5s+32YPiwuu4/CG1ly7PBwYQfyyuFhVg7khf8Hoa9AXjiGHKq+weu/oSI3Gg/0k4tH5n2yR+b9vI8Snt+RwxpUZiHiAVH2gOQQ5RJI3xcUG1B5iLIOtRwkrfxE4QEApfPUl76SxB0AAAAASUVORK5CYII=',
			'qrv2' => 'iVBORw0KGgoAAAANSUhEUgAAACEAAAAhAQMAAABtKlAsAAAABlBMVEUAAAD///+l2Z/dAAAAUElEQVR4nGP4DwQNDJjkB/k/7A0M3+///d7A8CX+rzgqCRH/IBrCDtd1H8kEotgf6oHk9+/1ILu+1oPMBLG/xH8AkyA1EPL7fYh7sLsTTAIA+1GCxeYLlGYAAAAASUVORK5CYII=',
			'qrv20' => 'iVBORw0KGgoAAAANSUhEUgAAAGkAAABpAQMAAAADTJ95AAAABlBMVEUAAAD///+l2Z/dAAAApElEQVR4nGP4jwwaGEjifpCHc56wNzB8vw/nfv3ewPAlHs59Ko7C/Qrlfqj//58dwgXp/Q7klv///xao94NoKAyEsEPshcqCnXEfrhebI++jckdlyZZlh4fzh3ogtxweC99B3HBg1AG5YApTFk3vIPHR8JMdjaPBJ/t1Pwr3QzxqOH9HiQVQKYoUR0glIUj2C6pepFIUZDIaF6kERiufSS7bUbkAZY5DRjGALDoAAAAASUVORK5CYII=',
			'qrv21' => 'iVBORw0KGgoAAAANSUhEUgAAAG0AAABtAQMAAACRNn0VAAAABlBMVEUAAAD///+l2Z/dAAAAtElEQVR4nGP4jwx+MJDE/SAP5/w5UMHw/T5C8n4Fw5d4OO/fRRTuHwj3j/z/D/X/D/7/GwjW+/f+/+/1/+/+/wfU+0E0FA4cKkD2wmTBzrgP14vNkfdRucNelh0RGuxAbjkirMqB3PD/f+P/f63/f/X//3BMWTS9g8RHAyU7GpLUkh0NSaJlv8WjcL/sR1Ucjxoa31HCClgCI4ckUikKkv2CqvcLmkWoXOTSG7VsJ7leQOUCANYhmxmTBIe0AAAAAElFTkSuQmCC',
			'qrv22' => 'iVBORw0KGgoAAAANSUhEUgAAAHEAAABxAQMAAADYsN6TAAAABlBMVEUAAAD///+l2Z/dAAAApklEQVR4nGP4jwIaGEjlf5BH8O6wNzB8v4/g3/3ewPAlHsE/K47K/wrhf6iHoqfiYP3f66HoLVD/B9FQOAhhB9sPlwe75z5CP1b33kfjDzd5doT/P9QD+eWI8PkO4of//1oPR1jk0fUPNv8NdvnR8B9Y+dHwp6n8RzT5v/ao/H/xaOH3HTV8gfUDSvgjl+8g+S9o+pHrB5D56Hzk+gW9/iGn/kLjAwA0Mwi28cYsaAAAAABJRU5ErkJggg==',
			'qrv23' => 'iVBORw0KGgoAAAANSUhEUgAAAHUAAAB1AQMAAABKyjz/AAAABlBMVEUAAAD///+l2Z/dAAAAoUlEQVR4nGP4jwJ+MJDK/yCPxHWoYPh+H8H9G1/B8CUewf8TiMa/COGzQxFIHqS/HIr+3a9g+CAaigBA80H6YPJg99xH6Mfq3vto/FF5VD47UvixA/nlSOFbDuSH/0dCWOTR9Q82/w12+dHwH1j50fCnqfyPeFT+H3tU/sd4tPD7jhq+oPoFOfyR6weQ/Bc0/V/Q7EPnI9dPGPUXGfUfGh8AsZFlK+3jnj0AAAAASUVORK5CYII=',
			'qrv24' => 'iVBORw0KGgoAAAANSUhEUgAAAHkAAAB5AQMAAAAnNBwKAAAABlBMVEUAAAD///+l2Z/dAAAAvElEQVR4nGP4jwoaGMgQ+CCPxP3C3sDw/T6SwN/vDQxf4pEEzoqjCRyFCPwBGsP+//9BoBZxsBl/gcaU//9/F6gFaMYH0VAECGEHuwOhAuyw+0hmYHf6fXSBEa6CHSnEPtQDBcqRwvQ7SCD8/9/4/0Dy/9X//79iVYFhxqD17XBSMRpzQ1XFaMwNShV/5qML7EcT+HYfPdS/o8ULsCZEjTmUWgykAqWe+1CPVhOCbMEQQKlNMepbMutsdAEA/4f4zbKz0CQAAAAASUVORK5CYII=',
			'qrv25' => 'iVBORw0KGgoAAAANSUhEUgAAAH0AAAB9AQMAAAC1Tv5mAAAABlBMVEUAAAD///+l2Z/dAAAAu0lEQVR4nO2VsQ3CMBBF3WUCaq/CChQopceIu4wGjJCCEt0KLogMkq2Pk0jofLgKTUB256en09m/+Ar5eagVwGl2jWerPHHBWHU37B4OAmBYwAVwPYJGHJYZN8D3iIRAVrndkZ29nfd4G/NixGaUVycJqvG10fBfbxLoeC5dAi1wBcYEDNCWjI8Zm33tPxk1uV81anKbNEYtQDwJ8CT5617kMrVpllzWhJORdeU0I29TFEDeyLKzV/a+BC8ZY3Bb+k3wqAAAAABJRU5ErkJggg==',
			'qrv26' => 'iVBORw0KGgoAAAANSUhEUgAAAIEAAACBAQMAAADdbksTAAAABlBMVEUAAAD///+l2Z/dAAAAt0lEQVR4nGP4jwYaGMgT+SCPzD/D3sDw/T6yyNXvDQxf4pFFnoqjixyFirD///+hHky9FYeYU/7///d6MHUWaM4H0VAkEMIOcQ9CDdiF95HNweGL+xgio2pIVcOOHM4f6oEi5chx8R0kEv4fhL7WQymsajDNGfx+H8lqRuN9ZKoZjfdhp+bTfHSRL/boIj/uY8TXd/Q4BbUBUOMdte4GqUGt3z/Uo7cBwLZjiKC2JTDbG+S3WzBEAPCk5VV3owwNAAAAAElFTkSuQmCC',
			'qrv27' => 'iVBORw0KGgoAAAANSUhEUgAAAIUAAACFAQMAAABPFKl/AAAABlBMVEUAAAD///+l2Z/dAAAAtklEQVR4nGP4jwZ+MJAn8kEemf/PoYLh+31kkb/3Kxi+xKOouYgu8h8q8qH+/392MPXnIsSc70CRcjD1B2jOB9FQZAC0C6QZoQbswvvI5uDwxX0MkVE1A6WGHSW+2IEi5ShxWg4UCf///yuQCaOwqsE0Z2D9NaoGv5rReB+Zakbjfdip+S2PLvLVHl0EVJujxdd39DgFtSVQ4x21DQBSg9pOAJmD1pb4j00EtU2C2W4hv/2DIQIAYitlc7K3dssAAAAASUVORK5CYII=',
			'qrv28' => 'iVBORw0KGgoAAAANSUhEUgAAAIkAAACJAQMAAAAi6omKAAAABlBMVEUAAAD///+l2Z/dAAAAsUlEQVR4nGP4jw4aGMgW+iCPIvCGvYHh+30UobPfGxi+xKMI/RXHEHoKEfpQj0BfxcFmfa9HIJBZH0RDkUEIO9hdyKrATr2PYhYuD93HFBqhqthRwutDPVCoHCVUv4OEwv9/rUdG2FVhMWtQ+HEkqBqNx+GhajQeh4eq0Xgc5Kp+z8cQ+rQfQ+hfPGbYf8eIIWBrCD0e0VowIFVfMM1Caw2BbMQihNaywtL+oqgthykEAL6K28xpVI6wAAAAAElFTkSuQmCC',
			'qrv29' => 'iVBORw0KGgoAAAANSUhEUgAAAI0AAACNAQMAAACwkGvmAAAABlBMVEUAAAD///+l2Z/dAAAArUlEQVR4nGP4jw5+MJAt9EEeReCPQwXD9/uoQvEVDF/iUfUFYgj9vQgRYkegv4EQs8oR6M/9CoYPoqEoAGgjyAAkVWCn3kcxC5eH7mMKjaqiWBU7atizA4XKUWOoHCgU/h8VYVeFxaxB4ceRoGo0HoeHqtF4HB6qRuNxkKv6JI8h9Hk/htDHeMyw/44RQ6CWFVo8orWGQKq+YJqF3tj6j1UIrZWGpS1HUbsQUwgAuBtGI/5sXtsAAAAASUVORK5CYII=',
			'qrv3' => 'iVBORw0KGgoAAAANSUhEUgAAACUAAAAlAQMAAAD/ULJAAAAABlBMVEUAAAD///+l2Z/dAAAAT0lEQVR4nGP4DwQ/GDDJD/L/D1QwfL///34Fw5f4/xdRSYj4B9FQhwqYrvtIJlDGZgeS3/+Xg90Q/gNkF5ANtJcdTP6Hkd/vQ92J1f1gEgCIBqRbO+8EJQAAAABJRU5ErkJggg==',
			'qrv30' => 'iVBORw0KGgoAAAANSUhEUgAAAJEAAACRAQMAAAD5FshgAAAABlBMVEUAAAD///+l2Z/dAAAAyElEQVR4nO2WMQ4CIRBF6TiBsfQoXkJjucfYEm7iWTzAdm5pqLancENiNN9htxoGqm2MGSp4+fkMwxTfQCxvtrB44ORuvUmBsyl58+w4G/eSTSuLDm9ytcBtZeSXHD7k2gMPYCC/uDuzdbJLfUy31By4X/NtocJUZ6htrH/REet5n1NmF8zEOtAGI+jQ0NX8fum9qtM5UF1bp3OgOp2Dv9fNV8leR8liV/m3JP+XcqKYgzLXZV2Z/6KTOTHfW2Nl7qzl0615t8K+i+b7JmwH5H8AAAAASUVORK5CYII=',
			'qrv31' => 'iVBORw0KGgoAAAANSUhEUgAAAJUAAACVAQMAAABrbCoMAAAABlBMVEUAAAD///+l2Z/dAAAAxElEQVR4nO2WMQ4CIRBF6TiB9R5Fr2BhLDkGdHs1PYKFpeEKFBpMZP0CNjI7VGthzJBteHnMQmaKrzBbd7WEhYGgg1PRt+jpnbqalk2nPtPAEQgj0oC0fdezwAWIIyaPlOuF1a5dG1fv9+nVO/u2XvdtnmHifd/TpB86M0v6ZjPbo3xn4JaZyZuOx9X7pfeKJ3MgXt+TORBP5uDvvRq9CHusmbOG6Vuc97fkTjoHNCcWj+bJUo/mzvJfjtEcy+bdhfmZYS99hoUndLveOgAAAABJRU5ErkJggg==',
			'qrv32' => 'iVBORw0KGgoAAAANSUhEUgAAAJkAAACZAQMAAAAGkgr5AAAABlBMVEUAAAD///+l2Z/dAAAA0ElEQVR4nO2XMQ7CMAxFs+UEqCOXAjH2GB2Tm3AWBm4AY9UTIGVoZSFRGbtlqeNWlWAA4SxNv54cR/7Dj8N8RfemmLZCuvjooBHiDaJrSyGeC0XsXmIK+KDSHvGE2BdjTQjY06dCrImkmmmzn66dH/uckEPzjag5f03ZvJG/THox9xRIrIRDgMUDeYrEEnl35Z85Uq35hXc3cg1pDjFymTSHGLlMmkP+kuw08q6R7VGbJihz50SdOSRLv0xmOTkFJVEPp2tiltLVPP+BN4ImPgG/xid8Jp8mGwAAAABJRU5ErkJggg==',
			'qrv33' => 'iVBORw0KGgoAAAANSUhEUgAAAJ0AAACdAQMAAACU6OiVAAAABlBMVEUAAAD///+l2Z/dAAAAvklEQVR4nGP4jwl+MFAo+EEeXexABcP3+2hi/+IrGL7Eown+uYhHkP3//w/1MOrvRYiZ5f//f6+HU0AzP4iGogGHCrA7UVWCHH8fzUzc3kR3/KhKeqlkR48jdqBgOXpslgMFw/+D0Nd6BIVLJVYzB6HfR1VSV+VoWhpVSS2Vo2lpVCW1VI6mpVGVeFX+icci+AObyt/zscX7dywpBNRKx0hLmC1qoEqMtjfITIxWOsh2rIIYLX/sfQTK+x3YBAGoBqgEbsZQeAAAAABJRU5ErkJggg==',
			'qrv34' => 'iVBORw0KGgoAAAANSUhEUgAAAKEAAAChAQMAAACVn031AAAABlBMVEUAAAD///+l2Z/dAAAAwUlEQVR4nO2XQQoCMQxFu+sJxKWXUlx6jFm2N/E0nmMuoBDBIaCLmqgMmn5dqBSRdFP6eIQG/uaHAk4On1OaWbaNOXBv6YZzOKws3U0RHW6UUilxvNTVuSzPbrwGmUuThTnzeP3vo3vZordzX2xcbeHu37nR5oGS0M5mh5UuJW/yvL+eunhu293c/R3Xc+ZuC9dz5m4L13Pm7pvuCbr7hOhxDfPAKDvaReqc1Z1B3bpfUEJdRP+Aad1xcB/6Ts+C9Ax4YVFegvBcpQAAAABJRU5ErkJggg==',
			'qrv35' => 'iVBORw0KGgoAAAANSUhEUgAAAKUAAAClAQMAAAAH5a+ZAAAABlBMVEUAAAD///+l2Z/dAAAAuklEQVR4nO2XsQ0CMQxF3WUCalZhBQpEmTGSjlGYiQmyQnQCHcWJEIvmzv9DAxII2XL19PUVx26+NFJXeZ/WtWXTJstYgJYs52jpbcvodHrQsGil6puWHbPU1c5Wf4M6Ga1OUazvi4lhCtd+TRtgb6HTBDtOne4b9FMt9/3df3Bt83twLVC/B9fOqd+Da+fU7+HvtUNktB4YvRzp3ka2Y804eA8ki3Qt5hb1xYyjb+AUsxPPWZ/Jb5TeAe6Sr8y1YXdqAAAAAElFTkSuQmCC',
			'qrv36' => 'iVBORw0KGgoAAAANSUhEUgAAAKkAAACpAQMAAABqG49sAAAABlBMVEUAAAD///+l2Z/dAAAAz0lEQVR4nO2XMQ7CMAxFs+UEiJFLgRh7jI7J1dhZO1a9QoSovICM3W71T8UQweJMydeTE8d/+YHRyqGJXE5GvMccaDLySDk8OyPPRygPq3xjLolfcklkPSgttUdmSvyWS3rWwyy1y+GyXee4vHtLL+1MpvZe87Ydp6P5wZJE7s1/k8pXHmROIncs+/VQpyu1/9Cl07+l3VVOt6fdVU63p91VTren3VVOf0E/ME2YloHDyRP0iaQ65CqQvJQGOa0kmOr0JRUZJMZKvmyWXbH8AYzzlgWNo1a1AAAAAElFTkSuQmCC',
			'qrv37' => 'iVBORw0KGgoAAAANSUhEUgAAAK0AAACtAQMAAAD4YW0AAAAABlBMVEUAAAD///+l2Z/dAAAA00lEQVR4nO2XOw7CMBBE3fkE1LkKV6BAlD6G0/lsHIGCEuUKlvgYCZxhnXTsOqIw0KyLSB49TbLZacZAOnfTRI4dE8d1b9LA5Ox6c3Hc4yDK4yw/yd4CeyCGcsmbyTuTvQdOQArThbzjassOfUl58ztdxhmY99LwfBylv0lbvh1Lsue79CTvKFygJ47ANcyXOl3x/sOUSv+W1lQp3Z7WVCndntZUKd2e1lQp/QH9cKJ8k+lzJ28+iTmhxiilSmp1RAsdsHhLRRJVWWijle669AcbyC+f/Cip4QSdmwAAAABJRU5ErkJggg==',
			'qrv38' => 'iVBORw0KGgoAAAANSUhEUgAAALEAAACxAQMAAACx586GAAAABlBMVEUAAAD///+l2Z/dAAAA0klEQVR4nO2XMQrDMAxFvfkEJWMu1dIxx8hoX62H6BgydTclwUuKKjmlQyV7aXAgyFP0eCiW+YsMiMebrXhoOZ2sN3HkfI7eTB3nQyPz+4ffAIKDBf9kU/Fo1v4DQHTwwq8+FdQ/nC7snO16/18/zTXy/sV3EOZS/0C+5XkIDnnP8xOJXzGnGD3kHXyLgp/rv9e86u/ra97Ur+lr3tSv6Wve1K/pa97U/8Nfcr6T+bPN5CHK+aF9WcqbtM+SL+2/wcn7Mt0nx6V9nLi0vxffcxv+Bl1q8DHjpSZ5AAAAAElFTkSuQmCC',
			'qrv39' => 'iVBORw0KGgoAAAANSUhEUgAAALUAAAC1AQMAAAAjnSzqAAAABlBMVEUAAAD///+l2Z/dAAAAy0lEQVR4nO2YsQ3DIBBF6ZggtVfJCi4il4wBnVfLCpmAFShikSL25XDliA9FZNlEOpqzvp5O3PGbb0XwvNReeuhydb47FT3AvVNPA/ge68tj1cNIpGlT3v3aP7JgaVuMU+Fyy8/VpfvnfJrL5/2re0BzCf87r8H+NesWvJdlfaCJvwb6KhW+1P+seYX/L178KXzLvPhT+JZ58afwLfPiT+EP5CeD9WXE+twV/BOx3ziPQ3/CvMw8ytepP8rj6T4lHeZ9wv8HqvvcR/8AS6x3b+hbFC8AAAAASUVORK5CYII=',
			'qrv4' => 'iVBORw0KGgoAAAANSUhEUgAAACkAAAApAQMAAACSrpK1AAAABlBMVEUAAAD///+l2Z/dAAAAVklEQVR4nGP4DwINDFipD/L//7A3MHy////v9waGL/H//4qjU1C5D6KhIewI7fdRDKMH70M9kPr+/3s92NVf68EuA/GA7vwAoUAqodT3+zD/4fQ7hAIAixfOUiSVhsgAAAAASUVORK5CYII=',
			'qrv40' => 'iVBORw0KGgoAAAANSUhEUgAAALkAAAC5AQMAAABOYwwfAAAABlBMVEUAAAD///+l2Z/dAAAAyklEQVR4nO2YsQrCQAyGb7snkI6+lOLoY3TMvUmfyk3p5B5BORA0Gtuh0j83FfQgRyHQj480139KEHxSWBDwGrw+xhRyD8Ahp3DdA3BqDHAeQRRhmpZHM/RoRTJNi/bg1XZ+NnGYY2Z8BuxBj/KVoAHdqMmI4J8zvUELUpIV7ESfG32XkmH2+OnkbtRueHbdqNXw7LpRq+HZdaNWw7Prxl8Yl84AT8u4d1auspFE3UzA7MKtgRpwz8BkbCb0q0wAtx8K4L6kfLuLgReQZDV0GOMEHgAAAABJRU5ErkJggg==',
			'qrv5' => 'iVBORw0KGgoAAAANSUhEUgAAAC0AAAAtAQMAAAAA1HDZAAAABlBMVEUAAAD///+l2Z/dAAAAVUlEQVR4nGP4DwI/GLBSH+T//z9QwfD9/v//9ysYvsT//38RnYLKfRANDXWogGu/j2LYgPPYgdT3///LIT4K/wF2NZAH8gM7hPoPp77fh/sdV7hAKAAMKvek396FvAAAAABJRU5ErkJggg==',
			'qrv6' => 'iVBORw0KGgoAAAANSUhEUgAAADEAAAAxAQMAAABJUtNfAAAABlBMVEUAAAD///+l2Z/dAAAAWklEQVR4nGP4DwYNDLjoD/L///9hb2D4fv///7/fGxi+xANpcUwaJv9BNDQ0hB3JnPto5g5B/od6IP39///v9ZDw+FoP8S+ID/L/BygNUg+jv99HhB++8IXSABpDKd6XTw/0AAAAAElFTkSuQmCC',
			'qrv7' => 'iVBORw0KGgoAAAANSUhEUgAAADUAAAA1AQMAAADbKDEzAAAABlBMVEUAAAD///+l2Z/dAAAAgUlEQVR4nGP4DwY/GHDRH+SBlEMFw/f7////u1/B8CUeSF+E0oFgmv1PIFi+/D9Q/oNoaGgoUD1QVznYnPv/2VHMvY9mD6V8dhAE0uUgCKTDQRCJD5Onln3xEPpHPYT+Fg81/zvEPmB4ge0HhweQ/wUq/wWqD0Z/v48UvnjCH0oDAEGdRpZ74EV3AAAAAElFTkSuQmCC',
			'qrv8' => 'iVBORw0KGgoAAAANSUhEUgAAADkAAAA5AQMAAAC21hHGAAAABlBMVEUAAAD///+l2Z/dAAAAk0lEQVR4nGP4DwENDHgYH+SB1Bf2Bobv94GMo98bGL7EAxlPxaGMtxDGwf9/xcFq7v6/C1TzQTQ0NDSEHWzOXYiB94FqUK24j2Ep7UTYgbZ/qAcyyoHu+Q5ihP+/+v8righcDe3d83s+lPEDJvLxPsz271D3AEMe4kJwqIJEwOH8oR4a8iBdcMZ3mBXg+CIQpzAGAF5Ci9Zhup/SAAAAAElFTkSuQmCC',
			'qrv9' => 'iVBORw0KGgoAAAANSUhEUgAAAD0AAAA9AQMAAAAkrPOqAAAABlBMVEUAAAD///+l2Z/dAAAAlElEQVR4nGP4DwE/GPAwPsgDqb8HKhi+3wcy/tyvYPgSD2T8uwhl/A0EMz7U/70IVvO9/i9QzQfRUCBwqACZ870ebOB9oBpUK+5jWEpPEXaQe9iBjHKQC8uBjPD/X+v/hyOLwNUMhAs/yUMZP2Ei/+7D3PMd6kJg7EDcDA55kAg4LkBqwLED0gVnfIdZ8UEe3S7cDABarr4ne1P6CQAAAABJRU5ErkJggg=='
		);

		private $counter;

		private $codeword_num;

		private $value;

		private $bits;
	
		/*
		 *	Constructor
		 */
		public function __construct($text, $size = 10, $ecc = 'M', $type = 'png', $file = null)
		{
			$this->setData($text);
			
			$this->setEcc($ecc);
			
			$this->setSize($size);
			
			$this->setType($type);
			
			isset($file) ? $this->draw($file) :	$this->draw();
		}
		
		/*
		 *	Set data to encode
		 *
		 *	@param	string
		 */
		public function setData($str)
		{
			if(empty($str))
			{
				throw new \Exception(__CLASS__.': Empty data to encode');
			}
			 
			$this->data = $str;
			
			$this->datalength = strlen($str);
			
			$this->counter = 0;
			
			$this->codeword_num = array();
			
			$this->value = array();
			
			$this->bits = array();
			
			$this->bits[$this->counter] = 4;
		}
		
		/*
		 *	Set error correction level
		 *
		 *	@param	string
		 */
		public function setEcc($ecc)
		{
			$ecc = strtolower($ecc);
			
			if(empty($ecc) or !array_key_exists($ecc, self::$ecc_hash))
			{
				throw new \Exception(__CLASS__.': Invalid correction error level');
			}
			
			$this->ecc = self::$ecc_hash[$ecc];
		}
		
		/*
		 *	Set image size
		 *
		 *	@param	int
		 */
		public function setSize($size)
		{
			if(empty($size) or ($size < 0 && $size > 50))
			{
				throw new \Exception(__CLASS__.': Invalid image size');
			}
			
			$this->size = $size;
		}
		
		/*
		 *	Set image type
		 *
		 *	@param	string
		 */
		public function setType($type)
		{
			if(empty($type) or ($type != 'png' && $type != 'jpeg'))
			{
				throw new \Exception(__CLASS__.': Invalid image type');
			}
			
			$this->type = $type;
		}
		
		/*
		 *	Draw QR Barcode
		 *
		 *	@param	string	Image name [optional]
		 */
		public function draw($file = null)
		{
			/*
			 *	@todo	Add caching
			 */
			$uniqid = array('qr', $this->data, $this->size, $this->ecc);
			
			if(\Nano\Cache::getInstance()->exists($uniqid))
			{
				
			}
			
			$codeword_counter = (preg_match('/[^0-9]/i', $this->data)) ? ((preg_match('/[^0-9A-Z \$\*\%\+\-\.\/\:]/i', $this->data)) ? $this->prepare8bit() : $this->prepareAlNum()) : $this->prepareNum();
	
			if(isset($this->bits[$this->counter]) && $this->bits[$this->counter] > 0)
			{
				$this->counter++;
			}

			$total_data_bits = 0;
				
			for($i=0; $i<$this->counter; $i++)
			{
				$total_data_bits += $this->bits[$i];
			}
			
			for($version=1, $i=1 + 40*$this->ecc; $i<40*(1 + $this->ecc); $i++, $version++)
			{
				if(self::$data_bits[$i] >= $total_data_bits+$this->codeword_num[$version])
				{
					$max_data_bits=self::$data_bits[$i];
					
					break;
				}
			}
	
			$total_data_bits += $this->codeword_num[$version];
			
			$this->bits[$codeword_counter] += $this->codeword_num[$version];
		
			$max_codewords_array = array
			(
				0, 26, 44, 70, 100, 134, 172, 196, 242, 
				292, 346, 404, 466, 532, 581, 655, 733, 815, 901, 991, 1085, 1156, 
				1258, 1364, 1474, 1588, 1706, 1828, 1921, 2051, 2185, 2323, 2465, 
				2611, 2761, 2876, 3034, 3196, 3362, 3532, 3706
			);
	
			$max_codewords = $max_codewords_array[$version];
			
			$max_modules_1side = 17 + ($version << 2);
		
			$matrix_remain_bit = array(0, 0, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, 0, 0, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, 0);
			
			$byte_num = $matrix_remain_bit[$version] + ($max_codewords << 3);
		
			$filename = APPLICATION.DS.'data'.DS.$this->path.'/qrv'.$version.'_'.$this->ecc.'.dat';
			
			if(!Filesystem::exists($filename))
			{
				throw new \Exception(__CLASS__.': QRV data file "'.$filename.'" does not exists');
			}
		
			$fp1 = fopen($filename, 'rb');
			
			$matx = fread($fp1, $byte_num);
			 
			$maty = fread($fp1, $byte_num);
			 
			$masks = fread($fp1, $byte_num);
			 
			$fi_x = fread($fp1, 15);
			 
			$fi_y = fread($fp1, 15);
			 
			$rs_ecc_codewords = ord(fread($fp1, 1));
			 
			$rso =	fread($fp1, 128);
			 
			fclose($fp1);
	
			$matrix_x_array = unpack('C*', $matx);
			
			$matrix_y_array = unpack('C*', $maty);
			
			$mask_array = unpack('C*', $masks);
	
			$rs_block_order = unpack('C*', $rso);
	
			$format_x2 = unpack('C*', $fi_x);
			
			$format_y2 = unpack('C*', $fi_y);
		
			$format_x1 = array(0, 1, 2, 3, 4, 5, 7, 8, 8, 8, 8, 8, 8, 8, 8);
			
			$format_y1 = array(8, 8, 8, 8, 8, 8, 8, 8, 7, 5, 4, 3, 2, 1, 0);
		
			$max_data_codewords = ($max_data_bits >> 3);
		
			$filename = APPLICATION.DS.'data'.DS.$this->path.'/rsc'.$rs_ecc_codewords.'.dat';
			
			if(!Filesystem::exists($filename))
			{
				throw new \Exception(__CLASS__.': RSC data file "'.$filename.'" does not exists');
			}
		
			$fp0 = fopen($filename, 'rb');
	
			for($i=0; $i<256; $i++)
			{
				$rs_cal_table_array[$i] = fread($fp0, $rs_ecc_codewords);
			}
	
			fclose($fp0);
		
			if($total_data_bits <= $max_data_bits - 4)
			{
				$this->value[$this->counter] = 0;
				
				$this->bits[$this->counter] = 4;
			}
			else
			{
				if($total_data_bits < $max_data_bits)
				{
					$this->value[$this->counter] = 0;
					
					$this->bits[$this->counter] = $max_data_bits - $total_data_bits;
				}
				else
				{
					if($total_data_bits > $max_data_bits)
					{
						throw new \Exception(__CLASS__.': Overflow');
					}
				}
			}
		
			$codewords_counter = 0;
			
			$codewords[0] = 0;
			
			$remaining_bits = 8;
	
			for($i=0; $i<=$this->counter; $i++)
			{
				$buffer = $this->value[$i];
				
				$buffer_bits = $this->bits[$i];
		
				$flag = 1;
					
				while($flag)
				{
					if($remaining_bits > $buffer_bits)
					{
						$codewords[$codewords_counter] = (($codewords[$codewords_counter] << $buffer_bits) | $buffer);
						
						$remaining_bits -= $buffer_bits;
						
						$flag = 0;
					}
					else
					{
						$buffer_bits -= $remaining_bits;
						
						$codewords[$codewords_counter] = (($codewords[$codewords_counter] << $remaining_bits) | ($buffer >> $buffer_bits));
	
						if($buffer_bits == 0)
						{
							$flag = 0;
						}
						else
						{
							$buffer = ($buffer & ((1 << $buffer_bits) - 1));
							
							$flag = 1;
						}
		
						$codewords_counter++;
						
						if($codewords_counter < $max_data_codewords - 1)
						{
							$codewords[$codewords_counter] = 0;
						}
	
						$remaining_bits = 8;
					}
				}
			}
			
			if($remaining_bits != 8)
			{
				$codewords[$codewords_counter] = $codewords[$codewords_counter] << $remaining_bits;
			}
			else
			{
				$codewords_counter--;
			}
		
			if($codewords_counter < $max_data_codewords - 1)
			{
				$flag = 1;
				
				while($codewords_counter < $max_data_codewords - 1)
				{
					$codewords_counter++;
					
					$codewords[$codewords_counter] = $flag ? 236 : 17;
					
					$flag = !$flag;
				}
			}
		
			$block_number = 0;
			
			$rs_temp[0] = '';
		
			for($i=0, $j=0; $i<$max_data_codewords; $i++)
			{
				$rs_temp[$block_number] .= chr($codewords[$i]);
					
				$j++;
		
				if($j >= $rs_block_order[$block_number + 1] - $rs_ecc_codewords)
				{
					$j = 0;
					
					$block_number++;
					
					$rs_temp[$block_number] = '';
					}
			}
	
			$count = count($rs_block_order);
			
			for($i=0; $i<$count; $i++)
			{
				$rs_codewords = $rs_block_order[$i + 1];
				
				$rs_data_codewords = $rs_codewords - $rs_ecc_codewords;
		
				$rstemp = $rs_temp[$i].str_repeat(chr(0), $rs_ecc_codewords);
				
					$padding_data = str_repeat(chr(0), $rs_data_codewords);

				for($j=$rs_data_codewords; $j>0; $j--)
				{
					$first = ord(substr($rstemp, 0, 1));

					if($first)
					{
						$left_chr = substr($rstemp, 1);
						
						$cal = $rs_cal_table_array[$first].$padding_data;
						
						$rstemp = $left_chr ^ $cal;
					 }
					 else
					 {
						$rstemp = substr($rstemp, 1);
					}
				}
		
				$codewords = array_merge($codewords, unpack('C*', $rstemp));
			}
		
			$matrix_content = array_fill(0, $max_modules_1side, array_fill(0, $max_modules_1side, 0));
		
			for($i=0; $i<$max_codewords; $i++)
			{
				$codeword_i = $codewords[$i];

				for($j=8; $j>=1; $j--)
				{
					$codeword_bits_number = ($i << 3) +	$j;
					
					$matrix_content[$matrix_x_array[$codeword_bits_number]][$matrix_y_array[$codeword_bits_number]] = ((255*($codeword_i & 1)) ^ $mask_array[$codeword_bits_number]);
					
					$codeword_i = $codeword_i >> 1;
				}
			}
		
			$matrix_remain = $matrix_remain_bit[$version];
			
			while($matrix_remain)
			{
				$remain_bit_temp = $matrix_remain + ($max_codewords << 3);
				
				$matrix_content[$matrix_x_array[$remain_bit_temp]][$matrix_y_array[$remain_bit_temp]] = (255 ^ $mask_array[$remain_bit_temp]);
				
				$matrix_remain--;
			}
		
			$min_demerit_score = 0;
			$hor_master = '';
			$ver_master = '';
		
			for($k=0; $k<$max_modules_1side; $k++)
			{
				for($l=0; $l<$max_modules_1side; $l++)
				{
					$hor_master .= chr($matrix_content[$l][$k]);
					$ver_master .= chr($matrix_content[$k][$l]);
				}
			}

			$all_matrix = $max_modules_1side*$max_modules_1side;
			
			for($i=0; $i<8; $i++)
			{
				$demerit_n1 = 0;
				
				$ptn_temp = array();
					
				$bit = 1 << $i;
				
				$bit_r = (~$bit) & 255;
				
				$bit_mask = str_repeat(chr($bit), $all_matrix);
				
				$hor = $hor_master & $bit_mask;
				
				$ver = $ver_master & $bit_mask;
		
				$ver_shift1 = $ver.str_repeat(chr(170), $max_modules_1side);
				
				$ver_shift2 = str_repeat(chr(170), $max_modules_1side).$ver;
					
				$ver_shift1_0 = $ver.str_repeat(chr(0), $max_modules_1side);
					
				$ver_shift2_0 = str_repeat(chr(0), $max_modules_1side).$ver;
					
				$ver_or = chunk_split(~($ver_shift1 | $ver_shift2), $max_modules_1side, chr(170));
					
				$ver_and = chunk_split(~($ver_shift1_0 & $ver_shift2_0), $max_modules_1side, chr(170));
		
				$hor = chunk_split(~$hor, $max_modules_1side, chr(170));
				
				$ver = chunk_split(~$ver, $max_modules_1side, chr(170));
				
				$hor = $hor.chr(170).$ver;
		
				$demerit_n3 = substr_count($hor,chr($bit_r).chr(255).chr($bit_r).chr($bit_r).chr($bit_r).chr(255).chr($bit_r))*40;
				
				$demerit_n4 = floor(abs(((100*(substr_count($ver, chr($bit_r))/($byte_num))) - 50)/5))*10;
		
				$demerit_n2 = 0;
				
				preg_match_all('/'.chr($bit_r).chr($bit_r).'+/', $ver_and, $ptn_temp);
				
				foreach($ptn_temp[0] as $str_temp)
				{
					$demerit_n2 += (strlen($str_temp) - 1);
				}

				$ptn_temp = array();
				 
				preg_match_all('/'.chr(255).chr(255).'+/', $ver_or, $ptn_temp);
				 
				foreach($ptn_temp[0] as $str_temp)
				{
					$demerit_n2 += (strlen($str_temp) - 1);
				}
				 
				$demerit_n2 *= 3;
		
				$ptn_temp = array();
		
				preg_match_all('/'.str_repeat(chr(255), 5).'+|'.str_repeat(chr($bit_r), 5).'+/', $hor, $ptn_temp);
				 
				foreach($ptn_temp[0] as $str_temp)
				{
					$demerit_n1 += (strlen($str_temp) - 2);
				}
			
				$demerit_score = $demerit_n1 + $demerit_n2 + $demerit_n3 + $demerit_n4;
		
				if($demerit_score <= $min_demerit_score or !$i)
				{
					$mask_number = $i;
					
					$min_demerit_score = $demerit_score;
				}
			}
		
			$mask_content=1 << $mask_number;

			$format_val = (($this->ecc << 3) | $mask_number);
					
			for($i=0; $i<15; $i++)
			{
				$content = substr(self::$format_inf[$format_val], $i, 1);
				
				$matrix_content[$format_x1[$i]][$format_y1[$i]] = $content*255;
					
				$matrix_content[$format_x2[$i + 1]][$format_y2[$i + 1]] = $content*255;
			}

			$mib = $max_modules_1side + 8;

			$image_size=$mib*$this->size;

			if($image_size > 1480)
			{
				throw new \Exception(__CLASS__.': Too large image');
			}
		
			$im = imagecreate($image_size, $image_size);
		
			$base_image = imagecreatefromstring(base64_decode($this->patterns['qrv'.$version]));
		
			$color = imagecolorallocate($base_image, 0, 0, 0);
			
			$mxe= 4 + $max_modules_1side;
		
			for($ii=0, $i=4; $i<$mxe; $i++, $ii++)
			{
				for($j=4, $jj=0; $j<$mxe; $j++, $jj++)
				{
					if($matrix_content[$ii][$jj] & $mask_content)
					{
						imagesetpixel($base_image, $i, $j, $color);
					}
				}
			}
		
			imagecopyresized($im, $base_image, 0, 0, 0, 0, $image_size, $image_size, $mib, $mib);
		
			$function = 'image'.$this->type;
		
			if(!isset($file))
			{
				Headers::add('Content-type: image/'.$this->type);
			}
			
			$function($im, $file);
		
			imagedestroy($base_image);
			
			imagedestroy($im);
		}
	
		/*
		 *	Prepare 8bit string
		 */
		private function prepare8bit()
		{
			$this->codeword_num = array
			 (
				0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
				8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 
				8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8
			);
	
			 $this->value[$this->counter] = 4;
			 
			 $this->value[++$this->counter] = $this->datalength;
	
			 $this->bits[$this->counter] = 8;
			 
			 $codeword_counter = $this->counter;
	
			 $this->counter++;
			 
			 for($i=0; $i<$this->datalength; $i++)
			 {
				$this->value[$this->counter] = ord(substr($this->data, $i, 1));
				
				$this->bits[$this->counter] = 8;
				
				$this->counter++;
			 }
			 
			return $codeword_counter;
		}
	 
		/*
		 *	Preapare string
		 */
		private function prepareAlNum()
		{
			$this->codeword_num = array
			(
				0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
				2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
				4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4
			);

			$this->value[$this->counter] = 2;
			
			$this->value[++$this->counter] = $this->datalength;
			 
			$this->bits[$this->counter] = 9;
			 
			$codeword_counter=$this->counter;
	
			$this->counter++;
			 
			for($i=0; $i<$this->datalength; $i++)
			{
				if(($i % 2) == 0)
				{
					$this->value[$this->counter]= self::$alphanumeric_hash[substr($this->data, $i, 1)];
					
					$this->bits[$this->counter] = 6;
				}
				else
				{
					$this->value[$this->counter] = $this->value[$this->counter]*45 + self::$alphanumeric_hash[substr($this->data, $i, 1)];
					
					$this->bits[$this->counter] = 11;
				 
					$this->counter++;
				}
			}
			 
			return $codeword_counter;
		}
		
		/*
		 *	Prepare numeric string
		 */
		private function prepareNum()
		{
			$this->codeword_num = array
			(
				0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
				2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 
				4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4
			);
	
			$this->value[$this->counter] = 1;
			 
			$this->value[++$this->counter] = $this->datalength;
			 
			$this->bits[$this->counter] = 10;
			 
			$codeword_counter = $this->counter;
	
			$this->counter++;

			for($i=0; $i<$this->datalength; $i++)
			{
				if(($i % 3) == 0)
				{
					$this->value[$this->counter]= substr($this->data, $i, 1);
					
					$this->bits[$this->counter] = 4;
				}
				else
				{
					$this->value[$this->counter] = $this->value[$this->counter]*10 + substr($this->data, $i, 1);
					
					if(($i % 3) == 1)
					{
						$this->bits[$this->counter] = 7;
					}
					else
					{
						$this->bits[$this->counter] = 10;
						
						$this->counter++;
					}
				}
			}
			
			return $codeword_counter;
		}
	}
}